/******************************************************/
/* Goblin's Cube - Rubik's cube game for Wii Homebrew */
/* Copyright (C) 2009 Ricardo Bueno Cordeiro          */
/* Licensed under de GNU General Public License v2.0  */
/*   See license_gplv2.txt for detalis                */
/******************************************************/

#include <string.h>

#include "sound_ogg_loader.h"

#include "vorbis/codec.h"
#include "vorbis/vorbisfile.h"

ov_callbacks SoundOggLoader::memory_access_callbacks = {
	&SoundOggLoader::read_func,
	&SoundOggLoader::seek_func,
	NULL,
	&SoundOggLoader::tell_func
};

size_t SoundOggLoader::read_func(void *ptr, size_t size, size_t nmemb, void *datasource) {
	SoundOggLoader *sol = (SoundOggLoader *)datasource;

	size_t to_copy = size * nmemb;
	if ((sol->current_pos + to_copy) > sol->file_size)
		to_copy = sol->file_size - sol->current_pos;

	const void *src = sol->file + sol->current_pos;
	memcpy(ptr, src, to_copy);
	sol->current_pos += to_copy;
	
	return to_copy;
}

int SoundOggLoader::seek_func(void *datasource, ogg_int64_t offset, int whence) {
	SoundOggLoader *sol = (SoundOggLoader *)datasource;
	
	switch(whence) {
		case SEEK_SET: sol->current_pos = offset; break;
		case SEEK_CUR: sol->current_pos += offset; break;
		case SEEK_END: sol->current_pos = sol->file_size + offset; break;
	};
	
	if (sol->current_pos > sol->file_size)
		sol->current_pos = sol->file_size;
	
	return 0;
}

long SoundOggLoader::tell_func(void *datasource) {
	SoundOggLoader *sol = (SoundOggLoader *)datasource;
	return sol->current_pos;
}

SoundOggLoader::SoundOggLoader(const u8 *f, u32 s) {
	this->file = f;
	this->file_size = s;
	this->current_pos = 0;

	if(ov_open_callbacks(this, &this->vf, NULL, 0, SoundOggLoader::memory_access_callbacks) < 0)
		return;
}

SoundOggLoader::~SoundOggLoader() {
	ov_clear(&this->vf);
}

u32 SoundOggLoader::get_size() {
	ogg_int64_t total = ov_pcm_total(&this->vf,-1);
	if (total == OV_EINVAL)
		return 0;
	return total * 2 * this->get_num_channel();
}

s32 SoundOggLoader::get_pitch() {
	vorbis_info *vi = ov_info(&this->vf,-1);
	if (vi == NULL)
		return 0;
	return vi->rate;
}

u16 SoundOggLoader::get_num_bits_per_sample() {
	return 16;
}

u16 SoundOggLoader::get_num_channel() {
	vorbis_info *vi = ov_info(&this->vf,-1);
	if (vi == NULL)
		return 0;
	return vi->channels;
}

void SoundOggLoader::get_samples(void *buffer, u32 start, u32 size) {
	char pcmout[4096];
	long ret;
	int current_bitstream;
	u8 *dest = (u8 *)buffer;

	if (ov_pcm_seek(&this->vf, start >> 1))
		return;

  while ((ret = ov_read(&vf, pcmout, sizeof(pcmout), 1, 2, 1, &current_bitstream)) && (size > 0)) {
		if (ret <= 0)
			break;
			
		if (size > ((u32)ret)) {
			memcpy(dest, pcmout, ret);
			dest += ret;
			size -= ret;
		} else {
			memcpy(dest, pcmout, size);
			dest += size;
			size = 0;
		}
	}
}
